package com.gmstu.springframework.aop.intercept;

import com.gmstu.springframework.aop.aspect.GpJoinPoint;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author John.zhang
 * @version Id: GpMethodInvocation, v 1.0 2020/8/26 17:00 ZhiYong Exp $
 */
public class GpMethodInvocation implements GpJoinPoint {

    private Object proxy;

    private Method method;

    private Object target;

    private Object[] arguments;

    private List<Object> interceptorsAndDynamicMethodMatcher;

    private Class<?> targetClass;

    private Map<String, Object> userAttributes;

    /**
     * 拦截器索引
     */
    private int currentInterceptorIndex = -1;

    public GpMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
                              Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatcher) {
        this.proxy = proxy;
        this.method = method;
        this.target = target;
        this.arguments = arguments;
        this.targetClass = targetClass;
        this.interceptorsAndDynamicMethodMatcher = interceptorsAndDynamicMethodMatcher;
    }

    public Object proceed() throws Throwable {
        //如果责任链执行完了则最后执行被代理方法
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatcher.size() - 1) {
            return this.method.invoke(this.target, this.arguments);
        }
        Object interceptorsAndDynamicInterceptorAdvice = this.interceptorsAndDynamicMethodMatcher.get(++this.currentInterceptorIndex);
        if (interceptorsAndDynamicInterceptorAdvice instanceof GpMethodInterceptor) {
            return ((GpMethodInterceptor) interceptorsAndDynamicInterceptorAdvice).invoke(this);
        }
        return proceed();
    }

    @Override
    public Object getThis() {
        return this.target;
    }

    @Override
    public Object[] getArguments() {
        return this.arguments;
    }

    @Override
    public Method getMethod() {
        return this.method;
    }

    @Override
    public void setUserAttribute(String key, Object value) {
        if (value != null) {
            if (this.userAttributes == null) {
                this.userAttributes = new HashMap<String, Object>(16);
            }
            this.userAttributes.put(key, value);
        }
    }

    @Override
    public Object getUserAttribute(String key) {
        return (this.userAttributes != null ? userAttributes.get(key) : null);
    }
}
